﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Smark.Data
{
    public class FieldInfo
    {
        public FieldInfo(string table, string name)
        {
            DBContext.Init();
            mTable = table;
            mName = name;
        }
        private string mTable;
        public string Table
        {
            get
            {
                return mTable;
            }
        }
        private string mName;
        public string Name
        {
            get
            {
                return mName;
            }
        }
        public Expression Eq(object value)
        {
           
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}=@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter
            {
                Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression LtEq(object value)
        {
            //string p = Expression.GetParamName();
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}<=@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter
            {
                Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression Lt(object value)
        {
           // string p = Expression.GetParamName();
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}<@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter
            {
                Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression Gt(object value)
        {
            //string p = Expression.GetParamName();
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}>@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter
            {
                Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression GtEq(object value)
        {
          
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}>=@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        public Expression NotEq(object value)
        {
         
            Expression exp = new Expression();
            string p = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0}<>@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        public Expression In(System.Collections.IEnumerable values)
        {
            string p;
            int i = 0;
            Expression exp = new Expression();
            exp.SqlText.Append(" ").Append( Name).Append(" in (");
            
            foreach (object value in values)
            {
                p = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(",");
                exp.SqlText.Append("@").Append(p);
                exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });

                i++;
            }
            exp.SqlText.Append(" )");
            return exp;
        }
        public Expression In(FieldInfo field, Expression expression)
        {
            Expression exp = new Expression();
            string astable = "T" + Expression.GetParamName();
            exp.SqlText.Append(" ").Append(Name).Append(" in (select ").Append(astable).Append(".").Append(field.Name).Append(" from ").Append(field.Table).Append(" ").Append( astable);
            if (expression != null && expression.SqlText.Length > 0)
            {
                exp.SqlText.Append(" where ").Append( expression.SqlText.ToString());
                exp.Parameters.AddRange(expression.Parameters);
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression NotIn(System.Collections.IEnumerable values)
        {
            string p;
            int i = 0;
            Expression exp = new Expression();
            exp.SqlText.Append(" ").Append(Name).Append(" not in (");
            foreach (object value in values)
            {
                p = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(",");
                exp.SqlText.Append("@").Append( p);
                exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });

                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression NotIn(FieldInfo field, Expression expression)
        {
            Expression exp = new Expression();
            string astable = "T" + Expression.GetParamName();
            exp.SqlText.Append(" ").Append(Name).Append(" not in (select ").Append(astable).Append(".").Append(field.Name).Append(" from ").Append( field.Table).Append( " ").Append(astable);
            if (expression != null && expression.SqlText.Length > 0)
            {
                exp.SqlText.Append(" where ").Append(expression.SqlText.ToString());
                exp.Parameters.AddRange(expression.Parameters);
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression Between(object fromvalue, object tovalue)
        {
            string p, p1;
           
            Expression exp = new Expression();
            p = Expression.GetParamName();
            p1 = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0} Between @{1} and @{2}", Name, p, p1));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, fromvalue) });
            exp.Parameters.Add(new Command.Parameter { Name = p1, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, tovalue) });
            return exp;
        }
        public Expression NotBetween(object fromvalue, object tovalue)
        {
            string p, p1;
           
            Expression exp = new Expression();
            p = Expression.GetParamName();
            p1 = Expression.GetParamName();
            exp.SqlText.Append(string.Format(" {0} not Between @{1} and @{2}", Name, p, p1));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, fromvalue) });
            exp.Parameters.Add(new Command.Parameter { Name = p1, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, tovalue) });
            return exp;
        }
        public Expression Like(object value)
        {
            if (value != null && value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return LikeMany((System.Collections.IEnumerable)value);
           
            Expression exp = new Expression();
            string pn = Expression.GetParamName();
            exp.SqlText.Append(
                string.Format("{0} like @{1}", Name, pn)
                );
            exp.Parameters.Add(new Command.Parameter { Name = pn, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        public FieldInfo Suffix(string name)
        {
            FieldInfo field = new FieldInfo(this.Table, this.Name);
            field.SuffixName = name;
            return field;
        }

        public string SuffixName
        {
            get;
            set;
        }
        private Expression LikeMany(System.Collections.IEnumerable value)
        {

            Expression exp = new Expression();
            int i = 0;
            string pn;
            exp.SqlText.Append("(");
            foreach (object item in value)
            {
                pn = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(" or ");
                exp.SqlText.Append(Name).Append(" like @").Append(pn);
                exp.Parameters.Add(new Command.Parameter { Name = pn, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, item) });
                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression Match(object value)
        {
            if (value != null && value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return MatchMany((System.Collections.IEnumerable)value);
            return Like("%" + value + "%");
        }
        private Expression MatchMany(System.Collections.IEnumerable value)
        {
            Expression exp = new Expression();
            int i = 0;
            string pn;
            exp.SqlText.Append("(");
            foreach (object item in value)
            {
                pn = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(" or ");
                exp.SqlText.Append(Name).Append(" like @").Append(pn);
                exp.Parameters.Add(new Command.Parameter { Name = pn, Value = "%" + item + "%" });
                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression IsNull()
        {
            Expression exp = new Expression();
            exp.SqlText.Append(" ").Append(Name).Append(" is null");
            return exp;
        }
        public Expression IsNotNull()
        {
            Expression exp = new Expression();
            exp.SqlText.Append(" ").Append(Name).Append(" is not null");
            return exp;
        }
        public override string ToString()
        {
            return Name;
        }
        
        public static Expression operator ==(FieldInfo field, object value)
        {
            if (value == null)
                return field.IsNull();
            if (value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return field.In((System.Collections.IEnumerable)value);
            if (value is InTableExpression)
            {
                InTableExpression ite = (InTableExpression)value;
                return field.In(ite.Field, ite.Expression);
            }
            return field.Eq(value);
        }
        public static Expression operator !=(FieldInfo field, object value)
        {
            if (value == null)
                return field.IsNotNull();
            if (value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return field.NotIn((System.Collections.IEnumerable)value);
            if (value is InTableExpression)
            {
                InTableExpression ite = (InTableExpression)value;
                return field.NotIn(ite.Field, ite.Expression);
            }
            return field.NotEq(value);
        }
        public static Expression operator >(FieldInfo field, object value)
        {
            return field.Gt(value);
        }
        public static Expression operator >=(FieldInfo field, object value)
        {
            return field.GtEq(value);
        }
        public static Expression operator <(FieldInfo field, object value)
        {
            return field.Lt(value);
        }
        public static Expression operator <=(FieldInfo field, object value)
        {
            return field.LtEq(value);
        }
        public FieldInfo At()
        {
            return new FieldInfo(Table, Table + "." + Name);
        }
        public FieldInfo At(string table)
        {
            return new FieldInfo(table, table + "." + Name);
        }

        public string Desc
        {
            get
            {
                return Name + " desc ";
            }
        }
        public string Asc
        {
            get
            {
                return Name + " asc ";
            }
        }
        public Field NewValue(object value)
        {

            return new Field { Name = Name, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) };
        }
        public Expression this[object min, object max]
        {
            get
            {
                return Between(min, max);
            }
        }
    
        public InTableExpression this[Expression exp]
        {
            get
            {
                InTableExpression intbl = new InTableExpression();
                intbl.Field = this;
                intbl.Expression = exp;
                return intbl;
            }
        }
        public JoinTable LJoin(FieldInfo field)
        {
            JoinTable jt = new JoinTable();
            jt.LJoin(this,field);
            return jt;
        }
        public JoinTable RJoin(FieldInfo field)
        {
            JoinTable jt = new JoinTable();
            jt.RJoin(this, field);
            return jt;
        }
        public JoinTable Join(FieldInfo field)
        {
            JoinTable jt = new JoinTable();
            jt.Join(this, field);
            return jt;
        }
    }
}
